package com.gooseeker.modbus;

import java.nio.ByteBuffer;

import org.apache.commons.lang.StringUtils;

public class CRC16Utils
{
	private static final short P_16_REFLECTED = (short) 0xA001;

	private static short[] crc_tab_8005_reflected = new short[256];

	static 
	{
		init_all_tab();
	}
	
	private static short rshiftu16(short value, int nb)
	{
		return (short) ((value >> nb) & ~(((short) 0x8000) >> (nb - 1)));
	}

	private static void init_crc16_reflected_tab(short[] table, short polynom)
	{
		int i, j;
		short crc16;

		for (i = 0; i < 256; i++)
		{
			crc16 = (short) i;

			for (j = 0; j < 8; j++)
			{
				if ((crc16 & 0x0001) != 0)
					crc16 = (short) (rshiftu16(crc16, 1) ^ polynom);
				else
					crc16 = rshiftu16(crc16, 1);
			}
			table[i] = crc16;
		}
	}

	private static void init_all_tab()
	{
		init_crc16_reflected_tab(crc_tab_8005_reflected, P_16_REFLECTED);
	}

	private static short update_crc16_reflected(short[] table, short crc, short c)
	{
		short short_c;

		short_c = (short) (0x00ff & c);

		return (short) (rshiftu16(crc, 8) ^ table[(crc ^ short_c) & 0xff]);
	}

	private static short update_crc16_A001(short crc, short c)
	{
		return update_crc16_reflected(crc_tab_8005_reflected, crc, c);
	}

	private static short calculate_crc16_Modbus(byte[] p, int length)
	{
		short crc;
		int i;

		crc = (short) 0xFFFF;

		for (i = 0; i < length; i++)
		{
			crc = update_crc16_A001(crc, p[i]);
		}
	
		return crc;
	}
	
	private static ByteBuffer string2Hex(String command)
	{
		String[] cmd = command.split(" ");
		int len = cmd.length;
		ByteBuffer bb = ByteBuffer.allocate(len + 2);
		
		char hi = 0;
		char lo = 0;
		byte temp = 0;
		for (int i = 0; i < len; i++)
		{
			hi = cmd[i].charAt(0);
			lo = cmd[i].charAt(1);
			
			temp = (byte)((charToHex(hi) << 4) | charToHex(lo));
			bb.put(temp);
		}
		
		return bb;
	}
	/**
	 * HEX字符串转为INT类型的值
	 * 仅限于2位和4位的字符串,例01，02，11等，转换为数字1，2，17
	 * @param hex
	 * @return
	 */
	public static short hexStrToInt(String hex)
	{
		short ret = 0;
		int len = hex.length();
				
		if (len == 2)
		{
			char hi = hex.charAt(0);
			char lo = hex.charAt(1);
			ret = (byte)((charToHex(hi) << 4) | charToHex(lo));
		}
		else if (len == 4)
		{
			char hi1 = hex.charAt(0);
			char lo1 = hex.charAt(1);
			char hi2 = hex.charAt(2);
			char lo2 = hex.charAt(3);
			
			ret = (short)((charToHex(hi1) << 12) | (charToHex(lo1) << 8)
					| (charToHex(hi2) << 4) | charToHex(lo2));
		}
		
		return ret;
	}
	
	public static String hexStrToIntStr(String hex)
	{
		short data = hexStrToInt(hex);
		
		return Integer.toString(data);
	}
	
	public static byte charToHex(char c)
	{
		char t = Character.toUpperCase(c);
		return (byte)"0123456789ABCDEF".indexOf(t);
	}
	/**
	 * 将数字转换为16进制字符串并格式化，如果不足两位则高位填充0
	 * @param b
	 * @return
	 */
	public static String intToHexStr(int b)
	{
		String ret = "";
		String hex = Integer.toHexString(b & 0xFF);
		if (hex.length() == 1)
		{
			hex = '0' + hex;
		}
		ret += hex.toUpperCase();
		
		return ret;
	}
	/**
	 * 字节数组转换为short
	 * 只支持1字节和2字节转换
	 * @param data
	 * @return
	 */
	public static short byteArrayToInt(byte[] data)
	{
		short ret = 0;
		
		int len = data.length;
		
		if (len == 1)
		{
			ret = (short)(data[0]);
		}
		else if (len == 2)
		{
			short hi = data[0];
			short lo = data[1];
			
			if (hi < 0)
			{
				hi = (short)(hi + 0xff);
			}
			
			if (lo < 0)
			{
				lo = (short)(lo + 0xff);
			}
			
			ret = (short)(hi << 8);
			ret += lo;
		}
		
		return ret;
	}
	
	public static byte[] modbusCrc16(String cmd)
	{
		if (StringUtils.isEmpty(cmd))
		{
			return null;
		}
		
		ByteBuffer bb = string2Hex(cmd);
		
		short crc16 = calculate_crc16_Modbus(bb.array(), bb.capacity() - 2);

		short swapCrc16 = Short.reverseBytes(crc16);
		bb.putShort(swapCrc16);
		
		return bb.array();
	}
	
	public static byte[] getCrc16(byte[] cmd, int len)
	{
		short crc16 = calculate_crc16_Modbus(cmd, len);
		
		byte[] ret = new byte[2];
		
		ret[0] = (byte)(crc16 & 0x00ff);
		ret[1] = (byte)((crc16 & 0xff00) >> 8);
		
		return ret;
	}

	public static void main(String[] args)
	{
		String cmdStr = "01 04 00 00 00 02";
		byte[] cmd = CRC16Utils.modbusCrc16(cmdStr);
		System.out.println(cmd[cmd.length - 1]);
		
		//byte[] bb = {1,4,0,0,0,2};
		
		//byte[] ret = CRC16Utils.getCrc16(bb, 6);
	
		System.out.println(CRC16Utils.intToHexStr('0'));
		
		byte[] test = {0x12, 0x32};
		System.out.println(CRC16Utils.byteArrayToInt(test));
	}
}
